指令的測試
在開始實現指令之前,我們一樣要來寫指令的測試。不過指令的測試其實是一件很不容易的事情,例如說我們今天要測試一道ADD指令,在RISC-V裡面的格式為 add rd,rs1,rs2,也就是將rs1 + rs2 放到 rd 那我們很簡單的會寫出下面的
測試。
reg[y] = 1
reg[z] = 2
TEST_EQUAL(add(reg[x],reg[y],reg[z]) , 3)
但這樣就能確保ADD指令正確嗎? 很難說,因為我們可能要考慮
等狀況,而如果是更複雜的指令,則遇到的問題可能更多,一個可能的解決方法是要找一份"解答"來對答案,例如說Spike。但如果我們不想要每次測試都將Spike起起來,我們可以使用RISC-V官方提供的一組測資來驗證指令行為,riscv-tests。
riscv-tests內提供各個組態下的多支測試程式,例如說rv32ui, rv64si, rv64ua, rv64uc等,
其中rv32/rv64很好可以理解,指的是支援多少bit的RISC-V指令,而之後的u/s/m指的是他要測什麼權限模式下的指令,u代表user, s代表supervisor, m代表machine。其中m的權限最大,s次之,再來則是u。在我們這系列中其實不會牽扯到太多部分,但可以簡單理解為m mode是硬體本身的ROM code及開機碼(boot code)所使用,而S通常指的是OS上面執行的系統層級的程式,u則是user的應用。最後i/a/c等代表的就是RISC-V的extension。
RISC-V Test安裝
git clone https://github.com/riscv/riscv-tests
cd riscv-tests
git submodule update --init --recursive
autoconf
./configure --prefix=$RISCV/target //install path
make
make install
RISC-V Test安裝很簡單,一樣是直接autoconf後make即可,make完成後會在isa資料夾底下生成多個小份的elf,以及對應的組合語言 (像是之前objdump的結果)
我們去細看他的原始碼內容,以ADD.S為例
#-------------------------------------------------------------
# Arithmetic tests
#-------------------------------------------------------------
TEST_RR_OP( 2, add, 0x00000000, 0x00000000, 0x00000000 );
TEST_RR_OP( 3, add, 0x00000002, 0x00000001, 0x00000001 );
TEST_RR_OP( 4, add, 0x0000000a, 0x00000003, 0x00000007 );
TEST_RR_OP( 5, add, 0xffffffffffff8000, 0x0000000000000000, 0xffffffffffff8000 );
TEST_RR_OP( 6, add, 0xffffffff80000000, 0xffffffff80000000, 0x00000000 );
TEST_RR_OP( 7, add, 0xffffffff7fff8000, 0xffffffff80000000, 0xffffffffffff8000 );
...
..
.
這些都是一個一個的巨集,而前面的2,3,4可以理解成題號,後面的add代表要測試的指令,接下來則是輸入以及答案,
而這些巨集實際上會被寫成指令,例如說TEST_RR_OP會被翻成,
#define TEST_RR_OP( testnum, inst, result, val1, val2 ) \
TEST_CASE( testnum, x14, result, \
li x1, MASK_XLEN(val1); \
li x2, MASK_XLEN(val2); \
inst x14, x1, x2; \
)
所以很不幸的,在我們什麼指令都還沒實現前其實是很難透過RISC-V Test進行測試的 (有點先有雞還先有蛋的問題),因此在最初可能還是要先寫一些很簡單的程式。
無論如何,我們都準備好測試程式了,接下來就會開始從RV64I的算術邏輯單元(ALU)相關指令 (加減、邏輯運算)等開始實作~
碎碎念 : 不知不覺就過了一半的時間了,祝大家中秋節快樂 :)